/*
 * Copyright © OpenAtom Foundation.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package io.iec.edp.caf.databaseobject;

import io.iec.edp.caf.app.manager.classloader.CAFClassLoader;
import io.iec.edp.caf.databaseobject.api.configuration.deplpyconfig.DboDeployConfiguration;
import io.iec.edp.caf.databaseobject.api.entity.DbType;
import io.iec.edp.caf.databaseobject.generate.DboModuleGenerator;
import io.iec.edp.caf.databaseobject.api.entity.AbstractDatabaseObject;
import io.iec.edp.caf.databaseobject.helper.DboDeployConfigHelper;
import io.iec.edp.caf.multicontext.classloader.PlatformClassloader;
import org.springframework.util.ClassUtils;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Stack;

/**
 * @author liu_wei
 */
public class DboDeployFileService {
    private DboDeployConfiguration configuration;

    public DboDeployFileService() {
        configuration = DboDeployConfigHelper.getInstance().getObjectNameMaxLengthConfiguration();
    }

    public void gernerateClass(AbstractDatabaseObject databaseObject, DbType dbType, String fileName, String generatorPath) {
        DboModuleGenerator generator = new DboModuleGenerator();
        generator.setAbstractDatabaseObject(databaseObject);
        generator.setDbType(dbType);
        generator.setFileName(fileName);
        generator.setGeneratorPath(generatorPath);
        generator.initialize();
        generator.generate();
    }

    public void compile(AbstractDatabaseObject databaseObject, DbType dbType, String generatorPath) {
        DboModuleGenerator generator = new DboModuleGenerator();
        generator.setAbstractDatabaseObject(databaseObject);
        generator.setDbType(dbType);
        ArrayList<String> jarPaths = new ArrayList<>();
        String runtimePath = System.getProperty("user.dir");
        jarPaths.add(Paths.get(runtimePath).resolve(configuration.getJarPath()).toString());
        generator.compile(Paths.get(runtimePath).resolve(configuration.getJavaPath()).resolve(generatorPath + "/dbo").toString(), Paths.get(runtimePath).resolve(configuration.getClassPath()).resolve(generatorPath).toString(), jarPaths);
    }

    protected void delFile(File file) {
        if (!file.exists()) {
            return;
        }
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if(files != null)
            {
                for (File f : files) {
                    delFile(f);
                }
            }
        }
        file.delete();
    }

    protected void deleteJavaFile(String runtimePath, String generatorPath) {
        DboDeployFileService fileService = new DboDeployFileService();
        File file = new File(Paths.get(runtimePath).resolve(configuration.getClassPath()).resolve(generatorPath).toString());
        fileService.delFile(file);
        File file1 = new File(Paths.get(runtimePath).resolve(configuration.getJavaPath()).resolve(generatorPath).toString());
        fileService.delFile(file1);
    }

    public void loadClass(String runtimePath, String generatorPath) throws Exception {
        File clazzPath = new File(Paths.get(runtimePath).resolve(configuration.getClassPath()).resolve(generatorPath).toString());
        // 设置class文件所在根路径
        // 例如/usr/java/classes下有一个test.App类，则/usr/java/classes即这个类的根路径，而.class文件的实际位置是/usr/java/classes/test/App.class
        //		File clazzPath = new File(class文件所在根路径);

        // 记录加载.class文件的数量
        int clazzCount = 0;
        //only handle the folder
        if (clazzPath.isFile()) {
            clazzPath = clazzPath.getParentFile();
        }

        if (!clazzPath.exists() && !clazzPath.isDirectory()) {
            return;
        }

        // 获取路径长度
        int clazzPathLen = clazzPath.getAbsolutePath().length() + 1;

        Stack<File> stack = new Stack<>();
        stack.push(clazzPath);

        // 遍历类路径
        while (!stack.isEmpty()) {
            File path = stack.pop();
            File[] classFiles = path.listFiles(new FileFilter() {
                @Override
                public boolean accept(File pathname) {
                    return pathname.isDirectory() || pathname.getName().endsWith(".class");
                }
            });
            ClassLoader loader = Thread.currentThread().getContextClassLoader();
            while ((loader.getParent() instanceof CAFClassLoader) || (loader instanceof CAFClassLoader)) {
                loader = loader.getParent();
            }
            if ((loader.getParent() instanceof PlatformClassloader)){
                loader = loader.getParent();
            }
            if(classFiles != null)
            {
                for (File subFile : classFiles) {
                    if (subFile.isDirectory()) {
                        stack.push(subFile);
                    } else {
                        if (clazzCount++ == 0) {
                            if(loader instanceof PlatformClassloader)
                            {
                                URL[] urls = {clazzPath.toURI().toURL()};
                                ((PlatformClassloader) loader).addPlatformURL(urls);
                            }
                            else
                            {
                                Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
                                method = ClassUtils.getMostSpecificMethod(method, URL.class);
                                boolean accessible = method.isAccessible();
                                try {
                                    if (!accessible) {
                                        method.setAccessible(true);
                                    }
                                    // 将当前类路径加入到类加载器中
                                    method.invoke(loader, clazzPath.toURI().toURL());
                                } finally {
                                    method.setAccessible(accessible);
                                }
                            }
                        }
                        // 文件名称
                        String className = subFile.getAbsolutePath();
                        className = className.substring(clazzPathLen, className.length() - 6);
                        className = className.replace(File.separatorChar, '.');
                        loader.loadClass(className);
                    }
                }
            }
        }
    }
}
